﻿using System;
using System.Linq;
using System.Security.Permissions;
using Ast.Framework.Environment.AutofacUtil;
using Ast.Framework.Logging;
using Autofac;
using NLog;
using NLog.Config;

namespace Ast.Framework.Environment.Extensions
{
    public class ExtensionLoader : MarshalByRefObject
    {
        private IHost _host;
        private IHostContainer _hostContainer;
        private IExtension _extension;

        public bool Initialize(ExtensionInitializeParameters extensionInitializeParameters)
        {
            // load extension
            using (var container = HostStarter.CreateHostContainer())
            {
                // set logger
                SetLogger(extensionInitializeParameters);

                var extensionManager = container.Resolve<IExtensionManager>();
                var extensionEntry = extensionManager.LoadExtension(extensionInitializeParameters.ExtensionName);
                if (extensionEntry == null) return false;
                if (!extensionEntry.ExportedTypes.Any(type => typeof (IExtension).IsAssignableFrom(type))) return false;

                // all good, load assemblies
                AutofacAssemblyStore.LoadAssembly("Ast.Framework");
                AutofacAssemblyStore.LoadAssembly("Ast.Core");
                AutofacAssemblyStore.LoadAssembly(extensionEntry.Assembly);
            }

            // create new container and resolve host
            _host = HostStarter.CreateHostContainer().Resolve<IHost>();
            _hostContainer = (IHostContainer) _host;

            // set logger for new container because LoggingFactoryModule will overwrite LogManager.Configuration
            SetLogger(extensionInitializeParameters);

            // create extension
            _extension = _hostContainer.Resolve<IExtension>();
            return true;
        }

        public ExtensionLoadResult Load()
        {
            _host.Activate();
            var retVal = _extension.Load(_hostContainer);
            return retVal;
        }

        public ExtensionUnLoadResult Unload()
        {
            var retVal = _extension.Unload();
            _host.Terminate();
            _host = null;
            return retVal;
        }

        private static void SetLogger(ExtensionInitializeParameters extensionInitializeParameters)
        {
            // add textbox log target and rule
            var logTarget = new LoggingMessageQueueTarget(extensionInitializeParameters.ExtensionName, extensionInitializeParameters.HostLoggingMessageQueue);
            var logRule = new LoggingRule("*", LogLevel.Info, logTarget); // info+

            // set nlog config
            var config = LogManager.Configuration;
            config.AddTarget("host", logTarget);
            config.LoggingRules.Add(logRule);
            LogManager.Configuration = config; // have to assign back to activate new rule and target
        }

        /// <summary>
        /// ref: http://stackoverflow.com/questions/2410221/appdomain-and-marshalbyrefobject-life-time-how-to-avoid-remotingexception
        /// Disables LifeTime service : object has an infinite life time until it's Disconnected.
        /// </summary>
        [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.Infrastructure)]
        public override object InitializeLifetimeService()
        {
            return null;
        }
    }

    public class ExtensionInitializeParameters : MarshalByRefObject
    {
        public string ExtensionName { get; set; }
        public ILoggingMessageQueue HostLoggingMessageQueue { get; set; }
    }

    public class ExtensionLoadResult : MarshalByRefObject
    {
        public bool CanUnload { get; set; }
    }

    public class ExtensionUnLoadResult : MarshalByRefObject
    {
    }
}